package flib;

import importExport.SVGLoader;
import importExport.SVGText;
import importExport.XmlInOut;

import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.Vector;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import processing.core.PApplet;

import simpleGeom.HasPos2D;
import simpleGeom.HasPos3D;
import simpleGeom.Polygon2D;
import simpleGeom.Vec2D;
import simpleGeom.Vec3D;
import simpleGui.FileHelper;

public class FloorplanLoader2D {

	TreeMap<String, WallType> walltypes;
	TreeMap<String, RoomType> roomtypes;
	HashMap<Stair, Vector<FlibWall>> stairPaths = new HashMap<Stair, Vector<FlibWall>>();
	Plan plan;
	
	public FloorplanLoader2D(File file){
		walltypes=loadWallTypes(file);
		roomtypes=loadRoomTypes(file);
	}
	
	
	public static TreeMap<String, WallType> loadWallTypes(File file) {
		TreeMap<String, WallType> walltypes = new TreeMap<String, WallType>();
		Document doc = XmlInOut.loadDoc(file);
		NodeList xmlWalltypes = doc.getElementsByTagName("wallType");
		for (int i = 0; i < xmlWalltypes.getLength(); i++) {
			Element xmlWallType = (Element) xmlWalltypes.item(i);
			WallType type = new WallType();
			type.load(xmlWallType);
			walltypes.put(type.label, type);
			
		}
		return walltypes;
	}

	public static TreeMap<String, RoomType> loadRoomTypes(File file) {
		TreeMap<String, RoomType> roomtypes = new TreeMap<String, RoomType>();
		Document doc = XmlInOut.loadDoc(file);
		NodeList xmlRoomtypes = doc.getElementsByTagName("roomType");
		for (int i = 0; i < xmlRoomtypes.getLength(); i++) {
			Element xmlRoomType = (Element) xmlRoomtypes.item(i);
			RoomType type = new RoomType();
			type.load(xmlRoomType);
			roomtypes.put(type.label, type);
		}
		return roomtypes;
	}

	public Plan loadFloorplanSVG(File file) {
		plan=new Plan();
		plan.roomtypes=roomtypes;
		plan.walltypes=walltypes;
		plan.path=file.getName();
		Document doc = XmlInOut.loadDoc(file);
		NodeList xmlLayers = doc.getElementsByTagName("layer");
		Vector<Element> layers = new Vector<Element>();
		for (int i = 0; i < xmlLayers.getLength(); i++) {
			Element xmlLayer = (Element) xmlLayers.item(i);
			String elName = xmlLayer.getAttribute("name");
			elName=elName.toLowerCase();
			if (elName.startsWith("level")) {
				layers.add(xmlLayer);
			}
		}
		for (int i = 0; i < layers.size(); i++) {
			Element xmlLayer = (Element) layers.get(i);
			String elName = xmlLayer.getAttribute("name");
			FlibLevel level = new FlibLevel();
			int iFloor = plan.levels.size();
			level.z = iFloor * 3;
			plan.levels.add(level);
			plan.nLevels++;
			String[] nameSegments = elName.split("-");

			int floor = Integer.valueOf(nameSegments[nameSegments.length - 1]);
			NodeList xmlPolys = xmlLayer.getElementsByTagName("polygon");

			Vector<FlibWall>stairPaths=new Vector<FlibWall>();
			Vector<Vector<Point2D>>areaPublic=new Vector<Vector<Point2D>>();
			NodeList xmlLines = xmlLayer.getElementsByTagName("line");
			for (int ii = 0; ii < xmlLines.getLength(); ii++) {
				Element xmlLine = (Element) xmlLines.item(ii);
				Line2D line = SVGLoader.getLine2D(xmlLine);
				String type = xmlLine.getAttribute("class");
				WallType wallType = walltypes.get(type);
				if (wallType==null){
					wallType=WallType.typeWall;
				}
				 if (type.equals("stair-path")) {
					FlibHENode n1 = new FlibHENode((float) line.getX1(),
							(float) line.getY1());
					FlibHENode n2 = new FlibHENode((float) line.getX2(),
							(float) line.getY2());
					NodeData nD1=new NodeData();
					n1.setData(nD1);
					NodeData nD2=new NodeData();
					n2.setData(nD2);
					FlibWall wall = new FlibWall(n1, n2);
					wall.levelId=iFloor;
					wall.type = wallType;
					stairPaths.add(wall);
				} else {
					FlibWall wall = level.addWall((float) line.getX1(),
							(float) line.getY1(), (float) line.getX2(),
							(float) line.getY2(), 0.02f);
					wall.levelId=iFloor;
					wall.type = wallType;
				}
			}
			
			level.createGraph();
			level.createRooms();
			/*for (int ii = 0; ii<level.rooms.size();ii++){
				Room room = level.rooms.get(ii);
				room.roomType=null;
				
			}*/
			for (int ii = 0; ii < xmlPolys.getLength(); ii++) {
				Element xmlPoly = (Element) xmlPolys.item(ii);
				Vector<Point2D> poly = SVGLoader.getPolygon(xmlPoly);
				String type = xmlPoly.getAttribute("class");
				
				RoomType roomType=roomtypes.get(type);
				for (int j = 0; j < level.rooms.size(); j++) {
					FlibFace room = level.rooms.get(j);
					room.levelId = iFloor;
					Vec2D v = room.getInnerPoint();
					// voila a problem...find a point inside
					if (Polygon2D.contains2D(v.x, v.y, poly)) {
						room.type = roomType;
						
					}
				}
				
			}
			/*for (int ii = 0; ii<level.rooms.size();ii++){
				Room room = level.rooms.get(ii);
				if (room.roomType==null){
					//room.detach();
					level.removeRoom(room);
					//level.rooms.remove(room);
					ii--;
				}
			}*/
			for (int ii=0;ii<stairPaths.size();ii++){
				FlibWall wall=stairPaths.get(ii);
				for (int j=0;j<level.rooms.size();j++){
					FlibFace room=level.rooms.get(j);
					Vector<FlibHENode>outline=room.getFaceNodes();
					if (Polygon2D.contains(wall.cX(),wall.cY(), outline)){
						room.steps=true;
					}
				}
			}
			for (int ii = 0; ii < level.nodes.size(); ii++) {
				FlibHENode n = level.nodes.get(ii);
				n.z = iFloor * 3;
			}
			
		}
		for (int i = 0; i < plan.levels.size() - 1; i++) {
			FlibLevel group1 = plan.levels.get(i);
			FlibLevel group2 = plan.levels.get(i + 1);
			group1.upGroup = group2;
			group2.downGroup = group1;
		}
		
		for (int i=0;i<plan.levels.size();i++){
			FlibLevel level=plan.levels.get(i);
			plan.nodes.addAll(level.nodes);
			plan.walls.addAll(level.walls);
			plan.rooms.addAll(level.rooms);
		}
		NodeList xmlTexts = doc.getElementsByTagName("text");
		for (int i=0;i<xmlTexts.getLength();i++){
			Element xmlText=(Element) xmlTexts.item(i);
			SVGText text=SVGLoader.getText(xmlText);
			for (int j=0;j<plan.rooms.size();j++){
				FlibFace room=plan.rooms.get(j);
				if (room.contains(text.getX(),text.getY())){
					//room.label=text;
				}
			}
		}
		
		return plan;
	}
	
	public static void saveAsGraph(FlibLevel level,Vector<Room>rooms,PrintWriter output){
		for (int i=0;i<level.nodes.size();i++){
			FlibHENode node=level.nodes.get(i);
			node.id=i;
			output.println("v "+node.x+" "+node.y+" "+node.z);
		}
		for (int i=0;i<level.walls.size();i++){
			FlibWall wall=level.walls.get(i);
			FlibHENode n1=wall.getN1();
			FlibHENode n2=wall.getN2();
			String lineString="l "+n1.id+" "+n2.id;
			lineString+=" # "+wall.type.label;
			output.println(lineString);
		}
		for (int i=0;i<rooms.size();i++){
			Room room=rooms.get(i);
			Vector<FlibHENode>nodes=room.getFaceNodes();
			String faceString="f";
			for (int ii=0;ii<nodes.size();ii++){
				FlibHENode n=nodes.get(ii);
				faceString+=" "+n.id;
			}
			if (room.type!=null){
				faceString+=" # "+room.type.label;
			}
			output.println(faceString);
		}
	}
	public static void saveAsGraph(FlibGraph graph,File file){
		FileWriter fw;
		try {
			fw = new FileWriter(file);
			BufferedWriter bw = new BufferedWriter(fw);
			saveAsGraph(graph.nodes,graph.walls,graph.rooms,bw);
			bw.flush();
			bw.close();
			System.out.println("OBJ exported: " + file);
		}
		catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
	public static void saveAsGraph(Vector<FlibHENode> nodes,Vector<FlibWall> walls,Vector<FlibFace> rooms,BufferedWriter bw){
		try {
		for (int i = 0; i < nodes.size(); i++) {
			FlibHENode node = nodes.get(i);
			node.id = i + 1;
			bw.write("v " + node.x + " " + node.y + " " + node.z);
			bw.newLine();
		}
		for (int i = 0; i < walls.size(); i++) {
			FlibWall wall = walls.get(i);
			FlibHENode n1 = wall.getN1();
			FlibHENode n2 = wall.getN2();
			String lineString = "l " + n1.id + " " + n2.id;
			lineString += " # " + wall.getLabel();
			bw.write(lineString);
			bw.newLine();
		}
		for (int i = 0; i < rooms.size(); i++) {
			FlibFace room = rooms.get(i);
			Vector<FlibHENode> fnodes = room.getFaceNodes();
			String faceString = "f";
			for (int ii = 0; ii < fnodes.size(); ii++) {
				FlibHENode n = fnodes.get(ii);
				faceString += " " + n.id;
			}
			if (room.type != null) {
				faceString += " # " + room.getLabel();
			}
			bw.write(faceString);
			bw.newLine();
		}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static void saveAsGraph(Vector<FlibHENode> nodes,Vector<FlibWall> walls,Vector<FlibFace> rooms,File file){
		FileWriter fw;
		try {
			fw = new FileWriter(file);
			BufferedWriter bw = new BufferedWriter(fw);
			saveAsGraph(nodes,walls,rooms,bw);
			bw.flush();
			bw.close();
			System.out.println("OBJ exported: " + file);
		}
		catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}

	public  Plan loadFromOBJ( String path) {
		
		  
		String[] lines=loadLines(path);
		Plan plan = new Plan();
		plan.path=path;
		System.out.println("load : "+path);
		
		Vector<FlibHENode> nodes = new Vector<FlibHENode>();
		Vector<FlibWall> walls = new Vector<FlibWall>();
		Vector<FlibFace> rooms = new Vector<FlibFace>();
		for (int i = 0; i < lines.length; i++) {
			String line = lines[i];
			String[] segs = line.split(" ");
			String start = segs[0];
			if (start.equals("v")) {
				float x = Float.valueOf(segs[1]);
				float y = Float.valueOf(segs[2]);
				float z = Float.valueOf(segs[3]);
				FlibHENode node = new FlibHENode(x, y, z);
				nodes.add(node);
				node.id = nodes.size();
			}
		}
		
		for (int i = 0; i < lines.length; i++) {
			String line = lines[i];
			String[] segs = line.split(" ");
			String start = segs[0];
			if (start.equals("l")) {
				int id1 = Integer.valueOf(segs[1]);
				int id2 = Integer.valueOf(segs[2]);
				FlibHENode n1 = nodes.get(id1-1);
				FlibHENode n2 = nodes.get(id2-1);
				FlibWall wall = new FlibWall(n1, n2);
				if (segs[3].equals("#")) {
					String comment = segs[4];
					WallType type=walltypes.get(comment);
					//WallType type = WallType.getTypeByLabel(comment);
					if (type==null)System.out.println(comment);
					wall.type = type;
				}
				wall.hEdge.attach();
				walls.add(wall);
			}
		}
		for (int i = 0; i < lines.length; i++) {
			String line = lines[i];
			int pos=line.indexOf("#");
			String geom=line;
			 String comment="";
			if (pos>=0){
				 geom=line.substring(0, pos);
				 comment=line.substring(pos, line.length());
				
			}
			
			String[] segs = geom.split(" ");
			String start = segs[0];
			String[] segscomments = comment.split(" ");
			if (start.equals("f")) {
				Room room = new Room();
				try {
					Integer id1 = Integer.valueOf(segs[1]);
					Integer id2 = Integer.valueOf(segs[2]);
					FlibHENode n1 = nodes.get(id1-1);
					FlibHENode n2 = nodes.get(id2-1);
					FlibHEdge hEdge = n1.getHEdge(n2);
					room.setHEdge(hEdge);
					hEdge.setFaceToAll(room);
					rooms.add(room);
					if (segscomments.length>=2){
					String roomType=segscomments[1];
					room.type=roomtypes.get(roomType);
					 System.out.println("null:"+roomType);

					if (room.type==null){
						 System.out.println("null:"+roomType);
					}
					}
				} catch (NumberFormatException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		FlibLevel level=new FlibLevel();
		plan.nodes = nodes;
		plan.walls = walls;
		plan.rooms = rooms;
		level.nodes=nodes;
		level.walls = walls;
		level.rooms = rooms;
		plan.levels.add(level);
		return plan;
	}
	
	public static String[]loadLines(String path){
		String[] lines = null;
		BufferedReader reader;
		try {
			reader = new BufferedReader(new FileReader(path));
			int nLines = 0;
			while (reader.readLine() != null)
				nLines++;
			reader.close();
			
			lines = new String[nLines];
			reader = new BufferedReader(new FileReader(path));
			nLines = 0;
			String cLine=null;
			while ((cLine = reader.readLine()) != null){
				lines[nLines]=cLine;
				nLines++;
			}
				
			reader.close();
			
		} catch (FileNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return lines;
	}
	public TreeMap<String,FlibBuilding>loadBuildingsFromSVG(String path){
		TreeMap<String,FlibBuilding> buildings=new TreeMap<String,FlibBuilding>();
		Vector<File>svgFiles=FileHelper.getFilesFromFolder(path,".svg");
		for (int i=0;i<svgFiles.size();i++){
			File file=svgFiles.get(i);
			String name=file.getName();
			String buildingName=name.replace(".svg","").split("Layer")[0];
			FlibBuilding building=buildings.get(buildingName);
			if (building==null){
				building=new FlibBuilding();
				building.name=buildingName;
				buildings.put(buildingName, building);
			}
			Plan plan=new Plan();
			plan=loadFloorplanSVG(file);
			for (int ii=0;ii<plan.levels.size();ii++){
				FlibLevel level=plan.levels.get(ii);
				if (level.nodes.size()>0){
				level.filename=name;
				building.levels.add(level);
				building.levelMap.put(level.filename, level);
				}
			}
			if (plan.levels.size()==0){
				System.out.println("no Level: "+name);
			}
		}
		return buildings;
	}
	public TreeMap<String,FlibBuilding>loadBuildingsFromOBJ(String path){
		TreeMap<String,FlibBuilding> buildings=new TreeMap<String,FlibBuilding>();
		Vector<File>objFiles=FileHelper.getFilesFromFolder(path,".obj");
		for (int i=0;i<objFiles.size();i++){
			File file=objFiles.get(i);
			String name=file.getName();
			String buildingName=name.replace(".obj","").split("Layer")[0];
			FlibBuilding building=buildings.get(buildingName);
			if (building==null){
				building=new FlibBuilding();
				building.name=buildingName;
				buildings.put(buildingName, building);
			}
			FlibLevel level=new FlibLevel();
			FloorplanLoader2D.loadGraphFromOBJ(level, walltypes, roomtypes,file.getAbsolutePath());
			level.filename=name;
			building.levels.add(level);
			building.levelMap.put(level.filename, level);
		}
		return buildings;
	}
	public  static void loadGraphFromOBJ(FlibGraph plan,TreeMap<String,WallType>walltypes,TreeMap<String,RoomType>roomtypes, String path) {
		
		String[] lines = loadLines(path);
		Vector<FlibHENode> nodes = new Vector<FlibHENode>();
		Vector<FlibWall> walls = new Vector<FlibWall>();
		Vector<FlibFace> rooms = new Vector<FlibFace>();
		for (int i = 0; i < lines.length; i++) {
			String line = lines[i];
			String[] segs = line.split(" ");
			String start = segs[0];
			if (start.equals("v")) {
				float x = Float.valueOf(segs[1]);
				float y = Float.valueOf(segs[2]);
				float z = Float.valueOf(segs[3]);
				FlibHENode node = new FlibHENode(x, y, z);
				nodes.add(node);
				node.id = nodes.size();
			}
		}
		for (int i = 0; i < lines.length; i++) {
			String line = lines[i];
			String[] segs = line.split(" ");
			String start = segs[0];
			if (start.equals("l")) {
				int id1 = Integer.valueOf(segs[1]);
				int id2 = Integer.valueOf(segs[2]);
				FlibHENode n1 = nodes.get(id1-1);
				FlibHENode n2 = nodes.get(id2-1);
				FlibWall wall = new FlibWall(n1, n2);
				if (segs[3].equals("#")) {
					String comment = segs[4];
					WallType type=walltypes.get(comment);
					//WallType type = WallType.getTypeByLabel(comment);
					if (type==null)System.out.println(comment);
					wall.type = type;
				}
				wall.hEdge.attach();
				walls.add(wall);
			}
		}
		for (int i = 0; i < lines.length; i++) {
			String line = lines[i];
			int pos=line.indexOf("#");
			String geom=line;
			 String comment="";
			if (pos>=0){
				 geom=line.substring(0, pos);
				 comment=line.substring(pos, line.length());
				
			}
			
			String[] segs = geom.split(" ");
			String start = segs[0];
			String[] segscomments = comment.split(" ");
			if (start.equals("f")) {
				Room room = new Room();
				try {
					Integer id1 = Integer.valueOf(segs[1]);
					Integer id2 = Integer.valueOf(segs[2]);
					FlibHENode n1 = nodes.get(id1-1);
					FlibHENode n2 = nodes.get(id2-1);
					FlibHEdge hEdge = n1.getHEdge(n2);
					room.setHEdge(hEdge);
					hEdge.setFaceToAll(room);
					rooms.add(room);
					if (segscomments.length>=2){
					String roomType=segscomments[1];
					room.type=roomtypes.get(roomType);
					if (room.type==null){
						 System.out.println("null:"+roomType);
					}
					}
				} catch (NumberFormatException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
		plan.nodes = nodes;
		plan.walls = walls;
		plan.rooms = rooms;

	}
}
